﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Linq;
using System.IO;
using System.Web.Security;

namespace PersonalAlbum
{

    public class UsersData
    {
        public const string RolesFileName = "Users.xml";
        private XDocument _users;
        public static string RolesFilePath
        {
            get
            {
                return HttpContext.Current.Server.MapPath("~/App_Data/" + RolesFileName);
            }
        }

        private XDocument Users
        {
            get
            {
                if (_users == null)
                {
                    lock (this)
                    {
                        if (!File.Exists(RolesFilePath))
                        {
                            _users = new XDocument(
                                new XDeclaration("1.0", "utf-8", "yes"),
                                new XElement("Users",
                                    new XElement("User",
                                        new XElement("Name", "Admin"),
                                        new XElement("Email", ""),
                                        new XElement("Password", "Admin"),
                                        new XElement("Roles", "Admin,User,Contributer,PowerUser"),
                                        new XElement("Approved",true))));
                            _users.Save(RolesFilePath);
                        }
                        else
                        {
                            _users = XDocument.Parse(File.ReadAllText(RolesFilePath));
                        }
                    }
                }
                return _users;
            }
        }

        public XElement Element(string name)
        {
            return this.Users.Element(name);
        }

        public IEnumerable<XElement> Descendants(string name)
        {
            return this.Users.Descendants(name);
        }

        public void Save()
        {
            Users.Save(RolesFilePath);
        }
    }

    public class RolesData
    {
        private string _rolesFileName = "Roles.xml";
        
        
        private string RolesPath
        {
            get
            {
                return HttpContext.Current.Server.MapPath("~/App_Data/" + _rolesFileName);
            }
        }

        private XDocument _roles;
        private XDocument Roles
        {
            get
            {
                if (_roles == null)
                {
                    lock (this)
                    {
                        if (!File.Exists(RolesPath))
                        {
                            _roles = new XDocument(
                                new XDeclaration("1.0", "utf-8", "yes"),
                                new XElement("Roles",
                                    new XElement("Role", "Admin"),
                                    new XElement("Role", "Contributer"),
                                    new XElement("Role", "PowerUser"),
                                    new XElement("Role", "User")));
                            _roles.Save(RolesPath);
                        }
                        else
                        {
                            _roles = XDocument.Parse(File.ReadAllText(RolesPath));
                        }
                    }
                }
                return _roles;
            }
        }

        public IEnumerable<XElement> Descendants(string name)
        {
            return this.Roles.Descendants(name);
        }

        public void Save()
        {
            Roles.Save(RolesPath);
        }

        public XElement Element(string name)
        {
            return this.Roles.Element(name);
        }
    }


    public class XmlRoleProvider : System.Web.Security.RoleProvider
    {
       
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            lock (this)
            {
                var usersFile = new UsersData();
                var l = new List<string>(usernames);
                var users = from u in usersFile.Descendants("User") where l.Contains(u.Element("Name").Value.Trim()) select u;
                foreach (var user in users)
                {
                    var roles = new List<string>();
                    if (user.Element("Roles") != null)
                    {
                        roles.AddRange(user.Element("Roles").Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
                    }
                    else
                    {
                        user.Add(new XElement("Roles", ""));//for the update
                    }
                    foreach (var role in roleNames)
                    {
                        if (!roles.Contains(role))
                        {
                            roles.Add(role);
                        }
                    }
                    user.Element("Roles").ReplaceWith(new XElement("Roles",
                        string.Join(",", roles.ToArray())));
                }
                usersFile.Save();
            }
        }

        public override string ApplicationName
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void CreateRole(string roleName)
        {
            lock (this)
            {
                var rolesFile = new RolesData();
                var current = (from r in rolesFile.Descendants("Role") where r.Value.Trim() == roleName select r).SingleOrDefault();
                if (current == null)
                {
                    rolesFile.Element("Roles").Add(new XElement("Role", roleName));
                    rolesFile.Save();

                }
            }
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {

            lock (this)
            {
                var rolesFile = new RolesData();
                var current = (from r in rolesFile.Descendants("Role") where r.Value.Trim() == roleName select r).SingleOrDefault();
                if (current != null)
                {
                    current.Remove();
                    rolesFile.Save();
                }
            }
            return true;
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            var usersFile = new UsersData();
            var users = from u in usersFile.Descendants("User") where 
                            u.Element("Roles")!=null && 
                            new List<string>(u.Element("Roles").Value.Split(new char[] { ',' })).Contains(roleName) && u.Element("Name").Value.Trim() == usernameToMatch 
                        select u.Element("Name").Value.Trim();

            return users.ToArray();
        }

        public override string[] GetAllRoles()
        {
            var rolesFile = new RolesData();
            return (from r in rolesFile.Descendants("Role") select r.Value.Trim()).ToArray();
        }

        public override string[] GetRolesForUser(string username)
        {
            var usersFile = new UsersData();

            var roles = (from u in usersFile.Descendants("User") where
                             u.Element("Roles")!=null && u.Element("Name").Value.Trim() == username 
                         select u.Element("Roles").Value).SingleOrDefault();
            if (roles == null)
            {
                return new string[] { };
            }
            else
            {
                return roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            }
        }

        public override string[] GetUsersInRole(string roleName)
        {
            var usersFile = new UsersData();

            var users = from u in usersFile.Descendants("User") where 
                            u.Element("Roles")!=null && 
                            new List<string>(u.Element("Roles").Value.Split(new char[] { ',' })).Contains(roleName)
                        select u.Element("Name").Value.Trim();

            return users.ToArray();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            return new List<string>(GetUsersInRole(roleName)).Contains(username);
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            lock (this)
            {
                var usersFile = new UsersData();
                var l = new List<string>(usernames);
                var users = from u in usersFile.Descendants("User") where l.Contains(u.Element("Name").Value.Trim()) select u;
                foreach (var user in users)
                {
                    if (user.Element("Roles") != null)
                    {
                        var roles = new List<string>(user.Element("Roles").Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
                        foreach (var role in roleNames)
                        {
                            if (roles.Contains(role))
                            {
                                roles.Remove(role);
                            }
                        }
                        user.Element("Roles").ReplaceWith(new XElement("Roles",
                            string.Join(",", roles.ToArray())));
                    }
                }
                usersFile.Save();
            }
        }

        public override bool RoleExists(string roleName)
        {
            return GetAllRoles().Contains(roleName);
        }
    }
}
